package com.sprite.utils.conversion;

import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;

import com.sprite.utils.UtilClass;
import com.sprite.utils.UtilString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sprite.utils.UtilMisc;
import com.sprite.utils.cache.UtilCache;

/**
 * @author Jack
 * @since 1.0.0
 */
public final class Converts {
    private static Logger logger = LoggerFactory.getLogger(Converts.class);
    private static final UtilCache<Converter<?, ?>> converterCache = UtilCache.createUtilCache("cache.converts");
    private static final String DELIMITER = "->";

    static {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Iterator<ConverterLoader> converterLoaders = ServiceLoader.load(ConverterLoader.class, loader).iterator();
        while (converterLoaders.hasNext()) {
            ConverterLoader converterLoader = converterLoaders.next();
            converterLoader.loadConverters();
        }
    }

    public static void loadContainedConverters(Class<?> containerClass) {
        for (Class<?> clz : containerClass.getClasses()) {
            if ((clz.getModifiers() & Modifier.ABSTRACT) == 0) {
                try {
                    Object value = clz.getConstructor().newInstance();
                    if (value instanceof Converter) {
                        Converts.registConverter((Converter<?, ?>) value);
                    }
                } catch (Exception e) {
                    logger.warn("[loadContainedConverters] register converter fail, {}", containerClass);
                }
            }
        }
    }

    public static void registConverter(Converter<?, ?> converter) throws ConversionException {
        Class sourceClass = converter.getSourceClass();
        Class targetClass = converter.getTargetClass();

        if (sourceClass.isPrimitive()) {
            throw new ConversionException(" source class should not be primitive");
        }

        // 如果目标类为 原始类型，转换方法必须给定初始值
        if (targetClass.isPrimitive() && converter.convert(null) == null) {
            throw new ConversionException(" target class is primitive, the value should not be null of the convert return");
        }

        String key = getKey(sourceClass, targetClass);
        converterCache.put(key, converter);


        if (UtilClass.isPrimitiveTypeWrapClass(sourceClass)) {
            Class primitiveClass = UtilClass.getPrimitiveClassOfPrimitiveTypeWrapClass(sourceClass);
            if (primitiveClass != null) {
                key = getKey(primitiveClass, targetClass);
                converterCache.put(key, converter);
            }
        }

    }

    /**
     * @param sourceClass
     *         原类型
     * @param targetClass
     *         目标类型
     * @param defaultConverter
     *         默认转换器
     * @param <S>
     *         泛型
     * @param <T>
     *         泛型
     * @return 匹配的转换器
     * @since 1.2.6
     */
    public static <S, T> Converter<S, T> getConverter(Class<S> sourceClass, Class<T> targetClass, Converter defaultConverter) {
        String key = getKey(sourceClass, targetClass);
        Converter converter = converterCache.get(key);

        if (converter == null) {
            converter = defaultConverter;
        }

        return converter;
    }

    private final static StringConverter stringConverter = new StringConverter();

    public static <S, T> Converter<S, T> getConverter(Class<S> sourceClass, Class<T> targetClass) {
        return getConverter(sourceClass, targetClass, null);
    }

    /**
     * @param sourceList
     *         原对象
     * @param targetClass
     *         目标类型
     * @param <T>
     *         泛型
     * @return 目标列表
     * @throws ConversionException
     *         转换异常
     * @since 1.2.7
     */
    public static <T> List<T> convert(Object[] sourceList, Class<T> targetClass) throws ConversionException {
        return convert(sourceList, targetClass, new LinkedList<>());
    }

    /**
     * @param sourceList
     *         原对象
     * @param targetClass
     *         目标类型
     * @param <T>
     *         泛型
     * @return 目标列表
     * @throws ConversionException
     *         转换异常
     * @since 1.2.7
     */
    public static <T> List<T> convert(List<Object> sourceList, Class<T> targetClass) throws ConversionException {
        return convert(sourceList, targetClass, new LinkedList<>());
    }

    /**
     * @param sourceList
     *         原对象
     * @param targetClass
     *         目标类型
     * @param appendList
     *         被追加数组
     * @param <T>
     *         泛型
     * @return 目标列表
     * @throws ConversionException
     *         转换异常
     * @since 1.2.7
     */
    public static <T> List<T> convert(Object[] sourceList, Class<T> targetClass, List<T> appendList) throws ConversionException {
        for (Object source : sourceList) {
            appendList.add(convert(source, targetClass));
        }

        return appendList;
    }

    /**
     * @param sourceList
     *         原对象
     * @param targetClass
     *         目标类型
     * @param appendList
     *         被追加数组
     * @param <T>
     *         泛型
     * @return 目标列表
     * @throws ConversionException
     *         转换异常
     * @since 1.2.7
     */
    public static <T> List<T> convert(List<Object> sourceList, Class<T> targetClass, List<T> appendList) throws ConversionException {
        for (Object source : sourceList) {
            appendList.add(convert(source, targetClass));
        }

        return appendList;
    }


    public static <T> T convert(Object source, Class<T> targetClass) throws ConversionException {
        if (source == null)
            return null;

        if (targetClass.isAssignableFrom(source.getClass())) {
            return UtilMisc.cast(source);
        }

        Converter<? extends Object, T> converter = null;

        if (String.class.isAssignableFrom(targetClass)) {
            converter = getConverter(source.getClass(), targetClass, stringConverter);
        } else {
            converter = getConverter(source.getClass(), targetClass);
        }

        if (converter == null) {
            throw new ConversionException("unsupport convert ：" + source.getClass() + "->" + targetClass.getName());
        }

        return converter.convert(UtilMisc.cast(source));
    }

    public static <T> T convert(Object source, Class<T> targetClass, T defaultValue) {
        if (source == null)
            return defaultValue;

        if (targetClass.isAssignableFrom(source.getClass())) {
            return UtilMisc.cast(source);
        }

        Converter<? extends Object, T> converter = null;

        if (String.class.isAssignableFrom(targetClass)) {
            converter = getConverter(source.getClass(), targetClass, stringConverter);
        } else {
            converter = getConverter(source.getClass(), targetClass);
        }

        if (converter == null) {
            return defaultValue;
        }

        T value = null;

        try {
            value = converter.convert(UtilMisc.cast(source));
        } catch (Exception e) {
        }

        if (value != null) {
            return value;
        }

        return defaultValue;
    }

    public static <S, T, K extends S> T convert(Class<S> sourceClass, Class<T> targetClass, K source, T defaultValue) {
        if (source == null)
            return defaultValue;

        if (targetClass.isAssignableFrom(source.getClass())) {
            return UtilMisc.cast(source);
        }

        Converter<? extends Object, T> converter = null;

        if (String.class.isAssignableFrom(targetClass)) {
            converter = getConverter(source.getClass(), targetClass, stringConverter);
        } else {
            converter = getConverter(source.getClass(), targetClass);
        }

        if (converter == null) {
            return defaultValue;
        }

        T value = null;

        try {
            value = converter.convert(UtilMisc.cast(source));
        } catch (Exception e) {
        }

        if (value != null) {
            return value;
        }

        return defaultValue;
    }

    private static String getKey(Class<?> sourceClass, Class<?> targetClass) {
        return getKey(sourceClass.getName(), targetClass.getName());
    }

    /**
     * @param sourceClassName
     *         类全名
     * @param targetClassName
     *         目标类全名
     * @return
     * @since 1.2.5
     */
    private static String getKey(String sourceClassName, String targetClassName) {
        return UtilString.place("{}{}{}", sourceClassName, DELIMITER, targetClassName);
    }

    private Converts() {
    }

}
